package ch.janotta.slawo.da;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.janotta.slawo.utilities.ModifiableValue;

public class Entity {
  private static final Logger LOGGER = LoggerFactory.getLogger(Entity.class);

  private Gender gender;
  private People people;
  private Background background;
  private CharacterClass characterClass;
  private Ability[] primaryAbilities;

  private int level;

  private Abilities abilities = new Abilities();

  private ModifiableValue health;
  private ModifiableValue mana;

  private List<Focus> communicationFocuses;
  private List<Focus> constitutionFocuses;
  private List<Focus> cunningFocuses;
  private List<Focus> dexterityFocuses;
  private List<Focus> magicFocuses;
  private List<Focus> perceptionFocuses;
  private List<Focus> strengthFocuses;
  private List<Focus> willpowerFocuses;

  private HashSet<Focus> allFocussesLuT;

  private List<Talent> talents;
  private List<Spell> spells;
  private List<SpecialPower> specialPowers;


  public Gender getGender() {
    return gender;
  }

  public void setGender(Gender gender) {
    this.gender = gender;
  }

  public People getPeople() {
    return people;
  }

  public void setPeople(People people) {
    this.people = people;
  }

  public Background getBackground() {
    return background;
  }

  public void setBackground(Background background) {
    this.background = background;
  }

  public CharacterClass getCharacterClass() {
    return characterClass;
  }

  public void setCharacterClass(CharacterClass characterClass) {
    this.characterClass = characterClass;
  }

  public void setPrimaryAbilities(Ability[] primaryAbilities) {
    this.primaryAbilities = primaryAbilities;
  }

  public boolean isPrimaryAbility(Ability ability) {
    return (Arrays.asList(primaryAbilities)).contains(ability);
  }

  public int getLevel() {
    return level;
  }

  public void setLevel(int level) {
    this.level = level;
  }

  public Ability getMaximumStartingAbility() {
    return abilities.getMaximumStartingAbility();
  }

  public int getAbilityValue(Ability ability) {
    int result = -99;
    AbilityValue value = abilities.get(ability);
    if (value != null) {
      result = value.getCurrentValue();
    }
    return result;
  }

  public void setAbilityValue(Ability ability, int value) {
    AbilityValue valueObject = abilities.get(ability);
    if (valueObject != null) {
      valueObject.setCurrentValue(value);
    }
  }

  public int getCommunication() {
    return getAbilityValue(Ability.Communication);
  }

  public void setCommunication(int communication) {
    setAbilityValue(Ability.Communication, communication);
  }

  public int getConstitution() {
    return getAbilityValue(Ability.Constitution);
  }

  public void setConstitution(int constitution) {
    setAbilityValue(Ability.Constitution, constitution);
  }

  public int getCunning() {
    return getAbilityValue(Ability.Cunning);
  }

  public void setCunning(int cunning) {
    setAbilityValue(Ability.Cunning, cunning);
  }

  public int getDexterity() {
    return getAbilityValue(Ability.Dexterity);
  }

  public void setDexterity(int dexterity) {
    setAbilityValue(Ability.Dexterity, dexterity);
  }

  public int getMagic() {
    return getAbilityValue(Ability.Magic);
  }

  public void setMagic(int magic) {
    setAbilityValue(Ability.Magic, magic);
  }

  public int getPerception() {
    return getAbilityValue(Ability.Perception);
  }

  public void setPerception(int perception) {
    setAbilityValue(Ability.Perception, perception);
  }

  public int getStrength() {
    return getAbilityValue(Ability.Strength);
  }

  public void setStrength(int strength) {
    setAbilityValue(Ability.Strength, strength);
  }

  public int getWillpower() {
    return getAbilityValue(Ability.Willpower);
  }

  public void setWillpower(int willpower) {
    setAbilityValue(Ability.Willpower, willpower);
  }

  public int getInitialHealth() {
    return health.getStartingValue();
  }

  public void setInitialHealth(int initialHealth) {
    this.health = new ModifiableValue(initialHealth);
  }

  public int getCurrentHealth() {
    return health.getCurrentValue();
  }

  public void setCurrentHealth(int currentHealth) {
    this.health.setCurrentValue(currentHealth);
  }

  public int getMaximumHealth() {
    return health.getMaximumValue();
  }

  public void setMaximumHealth(int maximumHealth) {
    this.health.setMaximumValue(maximumHealth);
  }

  public int getInitialMana() {
    return mana.getStartingValue();
  }

  public void setInitialMana(int initialMana) {
    this.mana = new ModifiableValue(initialMana);
  }

  public int getCurrentMana() {
    return mana.getCurrentValue();
  }

  public void setCurrentMana(int currentMana) {
    this.mana.setCurrentValue(currentMana);
  }

  public int getMaximumMana() {
    return mana.getMaximumValue();
  }

  public void setMaximumMana(int maximumMana) {
    this.mana.setMaximumValue(maximumMana);
  }

  public void addFocus(Focus focus) {
    if (focus == null) {
      String errorString = "Focus to add was null";
      LOGGER.error(errorString);
      throw new RuntimeException(errorString);
    }

    switch (focus.getAbility()) {
      case Communication:
        addCommunicationFocus(focus);
        break;
      case Constitution:
        addConstitutionFocus(focus);
        break;
      case Cunning:
        addCunningFocus(focus);
        break;
      case Dexterity:
        addDexterityFocus(focus);
        break;
      case Magic:
        addMagicFocus(focus);
        break;
      case Perception:
        addPerceptionFocus(focus);
        break;
      case Strength:
        addStrengthFocus(focus);
        break;
      case Willpower:
        addWillpowerFocus(focus);
        break;
    }
  }

  public boolean hasFocus(Focus focus) {
    return this.allFocussesLuT.contains(focus);
  }

  public void addTalent(Talent talent) {
    if (this.talents == null) {
      this.talents = new ArrayList<>();
    }
    this.talents.add(talent);
  }

  public boolean hasTalent(Talent talent) {
    return this.talents.contains(talent);
  }

  public void addSpell(Spell spell) {
    if (this.spells == null) {
      this.spells = new ArrayList<>();
    }
    this.spells.add(spell);
  }

  public boolean hasSpell(Spell spell) {
    return this.spells.contains(spell);
  }

  public void addSpecialPower(SpecialPower power) {
    if (this.specialPowers == null) {
      this.specialPowers = new ArrayList<>();
    }
    this.specialPowers.add(power);
  }

  public boolean hasSpetialPower(SpecialPower power) {
    return this.specialPowers.contains(power);
  }

  private void addCommunicationFocus(Focus communicationFocus) {
    doSafeAddFocus(this.communicationFocuses, communicationFocus);
  }

  private void addConstitutionFocus(Focus constitutionFocus) {
    doSafeAddFocus(this.constitutionFocuses, constitutionFocus);
  }

  private void addCunningFocus(Focus cunningFocus) {
    doSafeAddFocus(this.cunningFocuses, cunningFocus);
  }

  private void addDexterityFocus(Focus dexterityFocus) {
    doSafeAddFocus(this.dexterityFocuses, dexterityFocus);
  }

  private void addMagicFocus(Focus magicFocus) {
    doSafeAddFocus(this.magicFocuses, magicFocus);
  }

  private void addPerceptionFocus(Focus perceptionFocus) {
    doSafeAddFocus(this.perceptionFocuses, perceptionFocus);
  }

  private void addStrengthFocus(Focus strengthFocus) {
    doSafeAddFocus(this.strengthFocuses, strengthFocus);
  }

  private void addWillpowerFocus(Focus willpowerFocus) {
    doSafeAddFocus(this.willpowerFocuses, willpowerFocus);
  }

  private void doSafeAddFocus(List<Focus> aList, Focus aFocus) {
    if (aList == null) {
      aList = new ArrayList<>();
    }
    aList.add(aFocus);
    if (allFocussesLuT == null) {
      allFocussesLuT = new HashSet<>();
    }
    allFocussesLuT.add(aFocus);
  }

  public String toString() {
    String s = "\n";
    s += "This character is " + getIndefinitiveArticle(gender.name()) + " " + gender + " " + people + "\n";
    s += (gender == Gender.FEMALE ? "s" : "") +
        "he is " + getIndefinitiveArticle(background.name()) + " " + background + " " + characterClass + "\n";
    s += "Health: " + getPaddedNumber(health.getCurrentValue()) + "\n";
    s += "Mana:   " + getPaddedNumber(mana.getCurrentValue()) + "\n";
    s += "-------------------\n";
    s += "Communication: " + getAbilityOldNewValues(Ability.Communication) + "\n";
    s += "Constitution:  " + getAbilityOldNewValues(Ability.Constitution) + "\n";
    s += "Cunning:       " + getAbilityOldNewValues(Ability.Cunning) + "\n";
    s += "Dexterity:     " + getAbilityOldNewValues(Ability.Dexterity) + "\n";
    s += "Magic:         " + getAbilityOldNewValues(Ability.Magic) + "\n";
    s += "Perception:    " + getAbilityOldNewValues(Ability.Perception) + "\n";
    s += "Strength:      " + getAbilityOldNewValues(Ability.Strength) + "\n";
    s += "Willpower:     " + getAbilityOldNewValues(Ability.Willpower) + "\n";
    if (this.allFocussesLuT != null) {
      s += "-------------------\n";
      s += "Focusses:\n";
      for (Focus focus : this.allFocussesLuT) {
        s += focus.getAbility().name() + " (" + focus.name() + ")\n";
      }
    }
    if (this.talents != null) {
      s += "-------------------\n";
      s += "Talents:\n";
      for (Talent talent : this.talents) {
        s += talent.name() + "\n";
      }
    }
    if (this.spells != null) {
      s += "-------------------\n";
      s += "Spells:\n";
      for (Spell spell : this.spells) {
        s += spell.name() + "\n";
      }
    }
    return s;
  }

  private String getAbilityOldNewValues(Ability ability) {
    String oldValue = "??";
    String newValue = "??";
    AbilityValue abilityObject = abilities.get(ability);
    if (abilityObject != null) {
      oldValue = getPaddedNumber(abilityObject.getStartingValue());
      newValue = getPaddedNumber(abilityObject.getCurrentValue());
    }
    return oldValue + " / " + newValue;
  }

  private String getPaddedNumber(int number) {
    if (number < 0) {
      return "" + number;
    } else {
      return " " + number;
    }
  }

  private String getIndefinitiveArticle(String noun) {
    String article = "a";
    char firstChar = noun.toUpperCase().charAt(0);
    if (firstChar == 'A' || firstChar == 'E' || firstChar == 'I' || firstChar == 'O' || firstChar == 'U') {
      article += "n";
    }
    return article;
  }
}
